package com.mes.cloud.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.mes.cloud.dao.BmeBcProcessDAO;
import com.mes.cloud.dto.BmeBcProcessDTO;
import com.mes.cloud.service.BmeBcProcessService;
import com.mes.cloud.threadlocal.JdbcContext;
import com.mes.cloud.util.CommPager;
import com.mes.cloud.util.Compute;
import com.mes.cloud.util.JSONResult;
import com.mes.cloud.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * 流程配置
 *
 * @author wxm
 * @Time 2020-4-13
 */
@Service
public class BmeBcProcessServiceImpl implements BmeBcProcessService {

    private static final Logger LOGGER = LoggerFactory.getLogger(BmeBcProcessServiceImpl.class);

    //开始节点
    private static String TYPE_WORK_START = "work-start";

    //取值节点
    private static String TYPE_WORK_VALUE_GET = "work-value-get";

    //节点赋值
    private static String TYPE_WORK_VALUE_SET = "work-value-set";

    //节点判断
    private static String TYPE_JUDGE = "work-judge";

    //节点for
    private static String TYPE_FOR = "work-for";

    //节点判断
    private static String TYPE_CALCULATE = "work-calculate";

    private static String TYPE_WORK_END = "work-end";

    private static String GLOBAL_VARIABLE = "global";

    private static String GLOBAL_KEY_FORINDEX = "forIndex";

    @Autowired
    private BmeBcProcessDAO bmeBcProcessDAO;

    @Autowired
    @Qualifier("gengenJdbcTemplate")
    private JdbcTemplate jdbcTemplate;

    @Autowired
    @Qualifier("gengenCrmBaseJdbcTemplate")
    private JdbcTemplate gengenCrmBaseJdbcTemplate;

    @Autowired
    @Qualifier("gengenCrmCustomerJdbcTemplate")
    private JdbcTemplate gengenCrmCustomerJdbcTemplate;

    @Autowired
    @Qualifier("gengenCrmDictJdbcTemplate")
    private JdbcTemplate gengenCrmDictJdbcTemplate;

    private JdbcTemplate handTemplate;

    @Autowired
    private BcCRUDServiceImpl bcCRUDServiceImpl;

    @Override
    public List<BmeBcProcessDTO> findList(Map<Object, Object> condition) {
        return bmeBcProcessDAO.findList(condition);
    }

    @Override
    public long getCount(Map<Object, Object> condition) {
        return bmeBcProcessDAO.getCount(condition);
    }

    @Override
    public long insert(BmeBcProcessDTO bmeBcProcessDTO) {
        return bmeBcProcessDAO.insert(bmeBcProcessDTO);
    }

    @Override
    public long update(BmeBcProcessDTO bmeBcProcessDTO) {
        return bmeBcProcessDAO.update(bmeBcProcessDTO);
    }

    @Override
    public long delete(Integer id) {
        return bmeBcProcessDAO.delete(id);
    }

    @Override
    public BmeBcProcessDTO get(Integer id) {
        return bmeBcProcessDAO.get(id);
    }

    @Override
    public CommPager<BmeBcProcessDTO> findPageList(Map<Object, Object> condition) {
        int pageIndex = condition.get("pageIndex") == null ? 0 : Integer.valueOf(condition.get("pageIndex").toString());
        int pageSize = condition.get("pageSize") == null ? 10 : Integer.valueOf(condition.get("pageSize").toString());
        Page<BmeBcProcessDTO> page = PageHelper.startPage(pageIndex, pageSize, true);
        List<BmeBcProcessDTO> dictList = bmeBcProcessDAO.findList(condition);
        CommPager<BmeBcProcessDTO> pageResult = new CommPager<>(page, dictList);
        return pageResult;
    }

    @Override
    public BmeBcProcessDTO getByCode(String code) {
        return bmeBcProcessDAO.getByCode(code);
    }

    @Override
    public long enable(Map<Object, Object> map) {
        if (map.get("id") == null || map.get("isEnable") == null) {
            throw new RuntimeException("参数异常");
        }
        return bmeBcProcessDAO.enable(map);
    }

    @Override
    public void start(Map<Object, Object> map) {
        //全局变量map
        Map<String, Object> mapGlobal = new HashMap<>();
        Integer id = (Integer) map.get("id");
        LOGGER.info("执行节点开始ID："+ id);
        //获取流程信息
        BmeBcProcessDTO bmeBcProcessDTO = bmeBcProcessDAO.get(id);
        //获取流程配置
        String jsonConfigStr = bmeBcProcessDTO.getJsonconfig();
        LOGGER.info("流程jsonConfigStr:" + jsonConfigStr);
        JSONObject jsonConfig = JSON.parseObject(jsonConfigStr);
        JSONArray jsonNodeList = jsonConfig.getJSONArray("nodeList");
        JSONArray jsonLineList = jsonConfig.getJSONArray("lineList");
        if (jsonNodeList == null || jsonLineList == null) {
            return;
        }
        //获取启动节点
        JSONObject workStartNode = getWorkStartNode(jsonNodeList);
        LOGGER.info("流程workStartNode:" + workStartNode);
        initMapGlobal(workStartNode, mapGlobal);
        LOGGER.info("初始化值：" + mapGlobal);
        JSONObject firstNode = getNextNode(workStartNode, null, jsonNodeList, jsonLineList);
        startNode(firstNode, mapGlobal, jsonNodeList, jsonLineList);
        LOGGER.info("执行节点结束");
    }

    /**
     * 初始化全局变量
     * 启动节点设置的全局变量初始化到内存
     *
     * @param workStartNode
     */
    private void initMapGlobal(JSONObject workStartNode, Map<String, Object> mapGlobal) {
        LOGGER.info("启动节点设置的全局变量初始化到内存");
        JSONObject jsonNodeParameter = workStartNode.getJSONObject("parameter");
        JSONObject jsonNodeOut = jsonNodeParameter.getJSONObject("output");
        JSONArray jsonGlobalVariable = jsonNodeOut.getJSONArray(GLOBAL_VARIABLE);
        if (CollectionUtils.isEmpty(jsonGlobalVariable)) {
            return;
        }
        for (Object object : jsonGlobalVariable) {
            JSONObject tempJson = (JSONObject) object;
            mapGlobal.put(tempJson.getString("key"), tempJson.get("value"));
        }

    }


    //启动节点

    /**
     * @param jsonNode  节点信息
     * @param mapGlobal 全局变量
     */
    public void startNode(JSONObject jsonNode, Map<String, Object> mapGlobal, JSONArray jsonNodeList, JSONArray jsonLineList) {
        LOGGER.info("执行节点："+ jsonNode);
        //执行节点方法
        String nodeResult = startNodeMethod(jsonNode, mapGlobal);
        //获取下一个节点
        JSONObject jsonNextNode = getNextNode(jsonNode, nodeResult, jsonNodeList, jsonLineList);
        if (jsonNextNode == null) {
            throw new RuntimeException("获取下个节点失败" + jsonNode);
        }
        //判断是否是结束节点
        boolean isEnd = isEnd(jsonNextNode);
        if (!isEnd) {
            //执行下一个节点
            startNode(jsonNextNode, mapGlobal, jsonNodeList, jsonLineList);
        }

    }

    /**
     * 判断是否是终节点
     *
     * @param jsonNextNode
     * @return
     */
    private boolean isEnd(JSONObject jsonNextNode) {
        boolean isEnd = false;
        String type = jsonNextNode.getString("type");
        if (TYPE_WORK_END.equals(type)) {
            isEnd = true;
        }
        return isEnd;
    }

    /**
     * 获取下一个节点
     *
     * @param currentNode
     * @param nodeResult
     * @param jsonNodeList
     * @param jsonLineList
     */
    private JSONObject getNextNode(JSONObject currentNode, String nodeResult, JSONArray jsonNodeList, JSONArray jsonLineList) {
        JSONObject jsonNextNode = null;
        String nodeId = currentNode.getString("id");
        if (nodeId == null) {
            throw new RuntimeException("节点信息异常:" + currentNode);
        }
        JSONObject currentLine = getLineByFromId(jsonLineList, nodeId, nodeResult);
        if (currentLine == null) {
            throw new RuntimeException("节点获取连线失败:" + currentNode);
        }
        String toId = currentLine.getString("to_id");
        jsonNextNode = getNodeById(jsonNodeList, toId);
        return jsonNextNode;
    }

    /**
     * 获取节点
     *
     * @param jsonNodeList
     * @param nodeId
     * @return
     */
    private JSONObject getNodeById(JSONArray jsonNodeList, String nodeId) {
        JSONObject jsonObjectReturn = null;
        for (Object object : jsonNodeList) {
            jsonObjectReturn = (JSONObject) object;
            String id = jsonObjectReturn.getString("id");
            if (nodeId.equals(id)) {
                break;
            }
        }
        return jsonObjectReturn;
    }

    /**
     * 获取连线
     *
     * @param jsonLineList
     * @param fromId
     * @return
     */
    private JSONObject getLineByFromId(JSONArray jsonLineList, String fromId, String nodeResult) {
        JSONObject jsonObjectReturn = null;
        for (Object object : jsonLineList) {
            JSONObject jsonObjectTemp = (JSONObject) object;
            String fromIdTemp = jsonObjectTemp.getString("from_id");
            //连接线上面的值
            String value = jsonObjectTemp.getString("value");
            if (fromId.equals(fromIdTemp) && (nodeResult == null || nodeResult.equals(value))) {
                jsonObjectReturn = jsonObjectTemp;
                break;
            }
        }
        return jsonObjectReturn;
    }

    /**
     * 执行节点方法
     *
     * @param jsonNode
     * @param mapGlobal
     */
    private String startNodeMethod(JSONObject jsonNode, Map<String, Object> mapGlobal) {
        String type = jsonNode.getString("type");
        if (type == null) {
            throw new RuntimeException("节点类型信息异常" + jsonNode);
        }

        //取值
        if (TYPE_WORK_VALUE_GET.equals(type)) {
            return nodeGet(jsonNode, mapGlobal);
        }

        //设置值
        if (TYPE_WORK_VALUE_SET.equals(type)) {
            return nodeSet(jsonNode, mapGlobal);
        }
        if (TYPE_JUDGE.equals(type)) {
            return nodeJudge(jsonNode, mapGlobal);
        }

        if (TYPE_CALCULATE.equals(type)) {
            return nodeCalculate(jsonNode, mapGlobal);
        }

        if (TYPE_FOR.equals(type)) {
            return nodeFor(jsonNode, mapGlobal);
        }
        return null;

    }

    /**
     * for节点
     * @param jsonNode
     * @param mapGlobal
     * @return
     */
    private String nodeFor(JSONObject jsonNode, Map<String, Object> mapGlobal) {
        //判断全局变量中是否有 forIndex,如果没有赋值forIndex=0
        int forIndex = 0;
        if (mapGlobal.containsKey(GLOBAL_KEY_FORINDEX)) {
            forIndex = (int) mapGlobal.get(GLOBAL_KEY_FORINDEX);
            forIndex++;
        }
        mapGlobal.put(GLOBAL_KEY_FORINDEX, forIndex);

        List<Map<String, Object>> mapList = (List<Map<String, Object>>) mapGlobal.get("list");
        if(CollectionUtils.isEmpty(mapList)){
            return "N";
        }
        //遍历完成 调到N线
        if(forIndex > (mapList.size()-1)){
            return "N";
        }
        //获取第forIndex条数据
        Map<String, Object> mapOne = mapList.get(forIndex);
        JSONArray outputParams = getOutputParams(jsonNode);
        //查询的数据填充到全局变量
        appendMapGlobal(mapOne, outputParams, mapGlobal);
        return "Y";
    }

    /**
     * 执行取值节点方法
     *
     * @param jsonNode
     * @param mapGlobal
     * @return
     */
    public String nodeGet(JSONObject jsonNode, Map<String, Object> mapGlobal) {
        String bcCode = getBcCode(jsonNode);
        if (bcCode == null) {
            throw new RuntimeException("节点对应bcCode值为空:" + jsonNode);
        }
        String condition = getCondition(jsonNode);
        condition = replaceVar(condition, mapGlobal);
        Map baseMap = bcCRUDServiceImpl.getBcBase(bcCode);
        if (baseMap == null) {
            throw new RuntimeException("节点获取BC信息失败:" + jsonNode);
        }
        initHandTemplate(baseMap.get("bc_datasource").toString());
        String querysql = baseMap.get("bc_querysql").toString();
        if (baseMap.get("bc_filter") == null || baseMap.get("bc_filter").toString().isEmpty()) {
            querysql = querysql + " where 1=1 ";
        }
        if (!StringUtils.isEmpty(condition)) {
            querysql = querysql + " and " + condition;
        }
        LOGGER.info("querysql:" + querysql);
        List<Map<String, Object>> mapList = JdbcContext.getJdbcTemplate().queryForList(querysql);
        //判断是否是数据集
        boolean isList = isList(jsonNode);
        //数据集存放查询的list到全局变量
        if (isList) {
            mapGlobal.put("list", mapList);
        } else if (!CollectionUtils.isEmpty(mapList)) {
            //获取第一条数据
            Map<String, Object> mapOne = mapList.get(0);
            JSONArray outputParams = getOutputParams(jsonNode);
            //查询的数据填充到全局变量
            appendMapGlobal(mapOne, outputParams, mapGlobal);
        }
        return null;
    }

    /**
     * 节点判断目前做for处理
     * @param jsonNode
     * @param mapGlobal
     * @return
     */
    public String nodeJudge(JSONObject jsonNode, Map<String, Object> mapGlobal) {
        String judgeResult = "N";
        String expression = getExpression(jsonNode);
        expression = replaceVar(expression, mapGlobal);
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("js");
        try {
            boolean eval = (boolean) engine.eval(expression);
            if(eval){
                judgeResult = "Y";
            }
        } catch (ScriptException e) {
            LOGGER.error(e.getMessage());
        }
        return judgeResult;
    }

    public static void main(String[] args) {
        String expression = "2>3";
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("js");
        try {
            boolean eval = (boolean) engine.eval(expression);
        } catch (ScriptException e) {
            LOGGER.error(e.getMessage());
        }


    }

    /**
     * 执行节点赋值方法
     *
     * @param jsonNode
     * @param mapGlobal
     * @return
     */
    public String nodeSet(JSONObject jsonNode, Map<String, Object> mapGlobal) {
        //获取节点对应的BC
        String bcCode = getBcCode(jsonNode);
        if (bcCode == null) {
            throw new RuntimeException("节点对应bcCode值为空:" + jsonNode);
        }
        String condition = getCondition(jsonNode);
        condition = replaceVar(condition, mapGlobal);
        //获取BC信息
        Map baseMap = bcCRUDServiceImpl.getBcBase(bcCode);
        if (baseMap == null) {
            throw new RuntimeException("节点查询不到BC信息:" + jsonNode);
        }
        initHandTemplate(baseMap.get("bc_datasource").toString());
        JSONArray jsonArray = getValue(jsonNode);
        if (CollectionUtils.isEmpty(jsonArray)) {
            throw new RuntimeException("节点更新字段为空:" + jsonNode);
        }
        //组装更新字段
        String setValue = combination(jsonArray, mapGlobal);

        String updateSql = " update " + baseMap.get("bc_table").toString() + " set ";
        updateSql = updateSql + setValue;

        if (!StringUtils.isEmpty(condition)) {
            updateSql = updateSql + " where " + condition;
        }
        LOGGER.info("updateSql:" + updateSql);
        JdbcContext.getJdbcTemplate().update(updateSql);
        return null;
    }

    /**
     * 节点计算
     * @param jsonNode
     * @param mapGlobal
     * @return
     */
    public String nodeCalculate(JSONObject jsonNode, Map<String, Object> mapGlobal) {
        //获取输出参数
        JSONArray outputParams = getOutputParams(jsonNode);
        for (Object object : outputParams) {
            JSONObject jsonObject = (JSONObject) object;
            String setField = jsonObject.getString("set_field");
            //计算表达式
            String getField = jsonObject.getString("get_field");
            getField = replaceVar(getField, mapGlobal);
            mapGlobal.put(setField, Compute.compute(getField));
        }
        return null;
    }

    /**
     * 组合update set语句
     * 如果get_field字段值包含@ 需要取mapGlobal值做替换
     *
     * @param jsonArray
     * @param mapGlobal
     * @return
     */
    public String combination(JSONArray jsonArray, Map<String, Object> mapGlobal) {
        String setValue = "";
        if (CollectionUtils.isEmpty(jsonArray)) {
            return setValue;
        }
        for (Object object : jsonArray) {
            JSONObject jsonTemp = (JSONObject) object;
            String key = jsonTemp.getString("set_field");
            String value = jsonTemp.getString("get_field");
            //判断是否包含@
            if (value.contains("@")) {
                //获取mapGlobal中的值
                String mapKey = value.replace("@", "");
                if (mapGlobal.containsKey(mapKey)) {
                    value = mapGlobal.get(mapKey).toString();
                }
            }
            setValue = setValue+ key + "='" + value + "',";
        }
        if (setValue.length() > 0) {
            setValue = setValue.substring(0, setValue.length() - 1);
        }
        return setValue;
    }

    private String replaceVar(String condition, Map<String, Object> mapGlobal) {
        if (!mapGlobal.isEmpty() && !StringUtils.isEmpty(condition)) {
            Iterator<String> iter = mapGlobal.keySet().iterator();
            while (iter.hasNext()) {
                String key = iter.next();
                String value = "null";
                if(mapGlobal.get(key) != null){
                    value = mapGlobal.get(key).toString();
                }
                condition = condition.replace("@" + key, value);
            }
        }
        return condition;
    }

    /**
     * 更新组件 获取更新字段
     *
     * @param jsonNode
     * @return
     */
    private JSONArray getValue(JSONObject jsonNode) {
        JSONObject jsonNodeParameter = jsonNode.getJSONObject("parameter");
        JSONObject jsonNodeInput = jsonNodeParameter.getJSONObject("input");
        JSONArray jsonArray = jsonNodeInput.getJSONArray("params");
        return jsonArray;
    }

    /**
     * 获取参数
     *
     * @param jsonNode
     * @return
     */
    private String getCondition(JSONObject jsonNode) {
        JSONObject jsonNodeParameter = jsonNode.getJSONObject("parameter");
        JSONObject jsonNodeInput = jsonNodeParameter.getJSONObject("input");
        JSONObject jsonNodeBC = jsonNodeInput.getJSONObject("bc");
        String condition = jsonNodeBC.getString("condition");
        return condition;
    }

    /**
     * 获取判断组件的表达式
     *
     * @param jsonNode
     * @return
     */
    private String getExpression(JSONObject jsonNode) {
        JSONObject jsonNodeParameter = jsonNode.getJSONObject("parameter");
        JSONObject jsonNodeInput = jsonNodeParameter.getJSONObject("input");
        String expression = jsonNodeInput.getString("expression");
        return expression;
    }

    /**
     * 获取bcCode
     *
     * @param jsonNode
     * @return
     */
    private String getBcCode(JSONObject jsonNode) {
        JSONObject jsonNodeParameter = jsonNode.getJSONObject("parameter");
        JSONObject jsonNodeInput = jsonNodeParameter.getJSONObject("input");
        JSONObject jsonNodeBC = jsonNodeInput.getJSONObject("bc");
        String bcCode = jsonNodeBC.getString("code");
        return bcCode;
    }

    /**
     * 取值数据存放到全局变量
     *
     * @param mapOne
     * @param outputParams
     * @param mapGlobal
     */
    public void appendMapGlobal(Map<String, Object> mapOne, JSONArray outputParams, Map<String, Object> mapGlobal) {
        if (CollectionUtils.isEmpty(outputParams)) {
            return;
        }
        for (Object object : outputParams) {
            JSONObject jsonObject = (JSONObject) object;
            String setField = jsonObject.getString("set_field");
            String getField = jsonObject.getString("get_field");

            //判断是否包含@
            if (getField.contains("@")) {
                //获取mapGlobal中的值
                String mapKey = getField.replace("@", "");
                if (mapGlobal.containsKey(mapKey)) {
                    getField = mapGlobal.get(mapKey).toString();
                }
            }

            mapGlobal.put(setField, mapOne.get(getField));
        }
    }


    /**
     * 获取输出参数
     *
     * @param jsonNode
     * @return
     */
    private JSONArray getOutputParams(JSONObject jsonNode) {
        JSONObject jsonNodeParameter = jsonNode.getJSONObject("parameter");
        JSONObject jsonNodeOutput = jsonNodeParameter.getJSONObject("output");
        JSONArray outputParams = jsonNodeOutput.getJSONArray("params");
        return outputParams;
    }


    /**
     * 判断是否是数据集
     *
     * @param jsonNode
     * @return
     */
    private boolean isList(JSONObject jsonNode) {
        boolean isList = true;
        JSONObject jsonNodeParameter = jsonNode.getJSONObject("parameter");
        JSONObject jsonNodeOutput = jsonNodeParameter.getJSONObject("output");
        String isListValue = jsonNodeOutput.getString("isList");
        if ("false".equals(isListValue)) {
            isList = false;
        }
        return isList;
    }

    public JSONObject getWorkStartNode(JSONArray jsonNodeList) {
        JSONObject jsonObjectReturn = null;
        for (Object object : jsonNodeList) {
            jsonObjectReturn = (JSONObject) object;
            String type = jsonObjectReturn.getString("type");
            if (TYPE_WORK_START.equals(type)) {
                break;
            }
        }
        return jsonObjectReturn;
    }

    public void initHandTemplate(String dataSourceName) {
        if ("gengen".equals(dataSourceName)) {
//            this.handTemplate = jdbcTemplate;
            JdbcContext.setJdbcTemplate(jdbcTemplate);
            return;
        }
        if ("gengen_crm_base".equals(dataSourceName)) {
//            this.handTemplate = gengenCrmBaseJdbcTemplate;
            JdbcContext.setJdbcTemplate(gengenCrmBaseJdbcTemplate);
            return;
        }
        if ("gengen_crm_customer".equals(dataSourceName)) {
//            this.handTemplate = gengenCrmCustomerJdbcTemplate;
            JdbcContext.setJdbcTemplate(gengenCrmCustomerJdbcTemplate);
            return;
        }
        if ("gengen_crm_dict".equals(dataSourceName)) {
//            this.handTemplate = gengenCrmDictJdbcTemplate;
            JdbcContext.setJdbcTemplate(gengenCrmDictJdbcTemplate);
            return;
        }
//        this.handTemplate = jdbcTemplate;
        JdbcContext.setJdbcTemplate(jdbcTemplate);
        return;
    }
}
